using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using Oracle.ManagedDataAccess.Client;
using OracleInternal.Common;
using OracleInternal.MTS;
using OracleInternal.ServiceObjects;

namespace OracleInternal.ConnectionPool
{
	internal class TransactionContext<PM, CP, PR> where PM : PoolManager<PM, CP, PR>, new()where CP : Pool<PM, CP, PR>, new()where PR : PoolResource<PM, CP, PR>, new()
	{
		private const int s_maxBranchCount = 32;

		internal int m_maxBranchIndex;

		internal PR[] m_enlistedPRList;

		internal MTSTxnRM m_mtsTxnRM;

		internal object m_syncMTSTxnRM;

		internal string m_affinityInstanceName;

		internal string m_localTxnId;

		internal OraclePoolManager m_pm;

		internal object m_syncStats;

		internal List<string> m_instances;

		internal string m_id;

		public TransactionContext(PM pm, string localTxnId)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ctor, Trace.GetCPInfo(null, localTxnId, null, null));
			}
			try
			{
				m_localTxnId = localTxnId;
				m_enlistedPRList = new PR[33];
				m_syncMTSTxnRM = new object();
				m_id = GetHashCode().ToString();
				m_pm = pm as OraclePoolManager;
				m_syncStats = new object();
				m_instances = new List<string>();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ctor, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ctor, Trace.GetCPInfo(null, localTxnId, null, null));
				}
			}
		}

		public void ReInitialize(PM pm, Transaction txn)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ReInitialize, Trace.GetCPInfo(null, txn, null, null));
			}
			try
			{
				m_pm = pm as OraclePoolManager;
				m_localTxnId = txn.TransactionInformation.LocalIdentifier;
				lock (this)
				{
					for (int i = 0; i < m_enlistedPRList.Length; i++)
					{
						m_enlistedPRList[i] = null;
					}
				}
				m_affinityInstanceName = null;
				m_maxBranchIndex = 0;
				m_instances.Clear();
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ReInitialize, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.ReInitialize, Trace.GetCPInfo(null, txn, null, null));
				}
			}
		}

		internal MTSTxnRM GetRM(ConnectionString cs, string serviceName, string pdbName, Transaction txn)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetRM, Trace.GetCPInfo(null, txn, null, null));
			}
			try
			{
				if (m_mtsTxnRM == null)
				{
					lock (m_syncMTSTxnRM)
					{
						if (m_mtsTxnRM == null)
						{
							m_mtsTxnRM = MTSProxyPool.GetRM(cs.m_connectionPoolType == ConnectionPoolType.CCP, cs.ServerID, serviceName, pdbName, txn);
						}
					}
				}
				return m_mtsTxnRM;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetRM, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetRM, Trace.GetCPInfo(null, txn, null, null));
				}
			}
		}

		internal void RemoveRM()
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.RemoveRM, Trace.GetCPInfo(null, m_localTxnId, null, null) + "(txnid:" + m_localTxnId + ")");
			}
			try
			{
				PR val = null;
				for (int i = 0; i <= m_maxBranchIndex; i++)
				{
					val = m_enlistedPRList[i];
					try
					{
						if (val == null)
						{
							continue;
						}
						lock (val)
						{
							val.m_bTxnCtxPrimaryCon = false;
						}
						lock (this)
						{
							m_enlistedPRList[i] = null;
						}
						if (val.m_mtsTxnCtx != null && val.m_mtsTxnCtx.m_txnType == MTSTxnType.Distributed)
						{
							if (ProviderConfig.m_bTraceLevelPrivate)
							{
								string str = $"({val.m_endUserSessionId.ToString()}:{val.m_endUserSerialNum})";
								Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.PoolManager, OracleTraceFuncName.RemoveRM, str + " : (HA) Setting pr.m_deletionRequestor = DeletionRequestor.HA");
							}
							val.m_deletionRequestor = DeletionRequestor.HA;
						}
						if (val.m_bCheckedOutByApp || val.m_bPutCompleted || val.m_bCheckedOutByDTC)
						{
							continue;
						}
						lock (val)
						{
							if (!val.m_bCheckedOutByApp && !val.m_bPutCompleted && !val.m_bCheckedOutByDTC)
							{
								if (val.m_mtsTxnCtx != null)
								{
									val.m_mtsTxnCtx.m_mtsTxnBranch = null;
									(val as OracleConnectionImpl).ResetMTSTxnCtx();
								}
								val.m_txnCtx = null;
								try
								{
									val.m_pm.Put(val, null);
								}
								catch
								{
								}
							}
						}
					}
					catch
					{
					}
				}
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.RemoveRM, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.RemoveRM, Trace.GetCPInfo(null, m_localTxnId, null, null));
				}
			}
		}

		internal PR GetEnlisted(ConnectionString csWithDiffPassword, bool bGetForApp, OracleConnection connRefForCriteria, string instanceName = null, bool bEnforceAffinity = true)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(null, m_localTxnId, instanceName, null) + $"TxnCtx.GetEnlisted(inst={instanceName};enforce={bEnforceAffinity})");
			}
			PR val = null;
			_ = string.Empty;
			PR val2 = null;
			uint num = 0u;
			if (instanceName == null)
			{
				instanceName = m_affinityInstanceName;
			}
			try
			{
				bool flag = false;
				if (connRefForCriteria != null)
				{
					m_pm.m_criteriaMapper.AssignId(connRefForCriteria);
				}
				val = m_enlistedPRList[0];
				if (!CanReturnPR(val, connRefForCriteria))
				{
					val = null;
				}
				if (val != null && (!bEnforceAffinity || (bEnforceAffinity && val.m_instanceName == instanceName)) && !val.m_bCheckedOutByApp && !val.m_bPutCompleted && (csWithDiffPassword == null || (csWithDiffPassword.m_userId == val.m_cs.m_userId && ConnectionString.SecureStringsEqual(csWithDiffPassword.m_securedPassword, val.m_cs.m_securedPassword))))
				{
					lock (val)
					{
						if (!val.m_bCheckedOutByApp && !val.m_bPutCompleted && val.m_mtsTxnCtx != null)
						{
							lock (this)
							{
								if (val.m_mtsTxnCtx != null)
								{
									if (bGetForApp)
									{
										val.m_bCheckedOutByApp = true;
									}
									else
									{
										val.m_bCheckedOutByDTC = true;
										val.m_resPoolRefCount++;
									}
									flag = true;
								}
							}
						}
					}
				}
				if (flag)
				{
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, instanceName, "txnctx:get:res1"));
					}
					return val;
				}
				if (bEnforceAffinity)
				{
					val2 = null;
					num = 0u;
					for (int i = 1; i <= m_maxBranchIndex; i++)
					{
						val = m_enlistedPRList[i];
						if (val == null || val.m_bCheckedOutByApp || val.m_bPutCompleted || !(val.m_instanceName == instanceName) || (csWithDiffPassword != null && (!(csWithDiffPassword.m_userId == val.m_cs.m_userId) || !ConnectionString.SecureStringsEqual(csWithDiffPassword.m_securedPassword, val.m_cs.m_securedPassword))))
						{
							continue;
						}
						lock (val)
						{
							if (val.m_bCheckedOutByApp || val.m_bPutCompleted || val.m_mtsTxnCtx == null || val.m_txnCtx == null)
							{
								continue;
							}
							lock (val.m_txnCtx)
							{
								if (val.m_mtsTxnCtx == null)
								{
									continue;
								}
								if (connRefForCriteria == null)
								{
									val2 = val;
								}
								else
								{
									Pool<PM, CP, PR>.MatchCriteria(val, ref val2, ref num, connRefForCriteria, prForDTXN: true);
									val = val2;
								}
								if (connRefForCriteria == null || connRefForCriteria.m_bfoundPRMatchingAllCrit)
								{
									if (bGetForApp)
									{
										val.m_bCheckedOutByApp = true;
									}
									else
									{
										val.m_bCheckedOutByDTC = true;
										val.m_resPoolRefCount++;
									}
									flag = true;
									break;
								}
							}
						}
					}
					if (val2 != null)
					{
						if (connRefForCriteria != null && !connRefForCriteria.CanReturnBestMatchingPR())
						{
							val2 = null;
							val = null;
						}
					}
					else
					{
						val = null;
					}
					if (flag)
					{
						if (ProviderConfig.m_bTraceLevelPrivate)
						{
							Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, instanceName, "txnctx:get:res2"));
						}
						return val;
					}
					if (bEnforceAffinity)
					{
						return null;
					}
				}
				flag = false;
				val2 = null;
				num = 0u;
				for (int j = 1; j <= m_maxBranchIndex; j++)
				{
					val = m_enlistedPRList[j];
					if (val == null || val.m_bCheckedOutByApp || val.m_bPutCompleted || (csWithDiffPassword != null && (!(csWithDiffPassword.m_userId == val.m_cs.m_userId) || !ConnectionString.SecureStringsEqual(csWithDiffPassword.m_securedPassword, val.m_cs.m_securedPassword))))
					{
						continue;
					}
					lock (val)
					{
						if (val.m_bCheckedOutByApp || val.m_bPutCompleted || val.m_mtsTxnCtx == null || val.m_mtsTxnCtx.m_txnType != MTSTxnType.Distributed || val.m_txnCtx == null)
						{
							continue;
						}
						lock (val.m_txnCtx)
						{
							if (val.m_mtsTxnCtx == null || val.m_mtsTxnCtx.m_txnType != MTSTxnType.Distributed)
							{
								continue;
							}
							if (connRefForCriteria == null)
							{
								val2 = val;
							}
							else
							{
								Pool<PM, CP, PR>.MatchCriteria(val, ref val2, ref num, connRefForCriteria, prForDTXN: true);
								val = val2;
							}
							if (connRefForCriteria == null || connRefForCriteria.m_bfoundPRMatchingAllCrit)
							{
								if (bGetForApp)
								{
									val.m_bCheckedOutByApp = true;
								}
								else
								{
									val.m_bCheckedOutByDTC = true;
									val.m_resPoolRefCount++;
								}
								flag = true;
								break;
							}
						}
					}
				}
				if (val2 != null)
				{
					if (connRefForCriteria != null && !connRefForCriteria.CanReturnBestMatchingPR())
					{
						val2 = null;
						val = null;
					}
				}
				else
				{
					val = null;
				}
				if (flag)
				{
					if (ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, instanceName, "txnctx:get:res3"));
					}
					return val;
				}
				return null;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, ex);
				throw;
			}
			finally
			{
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					string arg = null;
					if (val == null)
					{
						arg = "null";
					}
					else if (val != null)
					{
						arg = ((!(val.m_instanceName == instanceName)) ? "matchfound=F" : "matchfound=T");
					}
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, instanceName, null, bResourcePoolInfo: true) + $"TxnCtx.GetEnlisted(inst={instanceName};enforce={bEnforceAffinity}) returning ({arg})");
				}
			}
		}

		internal PR GetEnlisted(string affinityInstance, int branchNum, bool bMustMatch, out bool bMatchFound)
		{
			if (ProviderConfig.m_bTraceLevelPrivate)
			{
				Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2304, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(null, m_localTxnId, null, null) + $"TxnCtx.GetEnlisted(aff={affinityInstance};br={branchNum};bMustMatch={bMustMatch})");
			}
			PR val = null;
			bMatchFound = false;
			_ = string.Empty;
			PR val2 = null;
			uint num = 0u;
			OracleConnection oracleConnection = null;
			try
			{
				if (oracleConnection != null)
				{
					m_pm.m_criteriaMapper.AssignId(oracleConnection);
				}
				val2 = null;
				num = 0u;
				if (branchNum >= 1 && branchNum <= 32)
				{
					val = m_enlistedPRList[branchNum];
					if (val != null && affinityInstance != null && val.m_instanceName == affinityInstance && !val.m_bPutCompleted)
					{
						lock (val)
						{
							if (!val.m_bPutCompleted && val.m_mtsTxnCtx != null && val.m_txnCtx != null)
							{
								lock (val.m_txnCtx)
								{
									if (val == m_enlistedPRList[branchNum] && val.m_mtsTxnCtx != null)
									{
										val.m_bCheckedOutByDTC = true;
										val.m_resPoolRefCount++;
										bMatchFound = true;
									}
								}
							}
						}
					}
				}
				if (!bMatchFound)
				{
					val = null;
				}
				if (bMustMatch | bMatchFound)
				{
					if (val != null && ProviderConfig.m_bTraceLevelPrivate)
					{
						Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, null, "txnctx:get:res1"));
					}
					return val;
				}
				val2 = null;
				num = 0u;
				int num2 = -1;
				for (int i = 1; i <= m_maxBranchIndex; i++)
				{
					val = m_enlistedPRList[i];
					if (val == null || affinityInstance == null || !(val.m_instanceName == affinityInstance) || val.m_bPutCompleted)
					{
						continue;
					}
					lock (val)
					{
						if (val.m_bPutCompleted || val.m_mtsTxnCtx == null || val.m_txnCtx == null)
						{
							continue;
						}
						lock (val.m_txnCtx)
						{
							if (val == m_enlistedPRList[i] && val.m_mtsTxnCtx != null)
							{
								if (oracleConnection == null)
								{
									val2 = val;
								}
								else
								{
									Pool<PM, CP, PR>.MatchCriteria(val, ref val2, ref num, oracleConnection, prForDTXN: true);
									val = val2;
								}
								if (val == val2)
								{
									num2 = i;
								}
								if (oracleConnection == null || oracleConnection.m_bfoundPRMatchingAllCrit)
								{
									val.m_bCheckedOutByDTC = true;
									val.m_resPoolRefCount++;
									break;
								}
							}
						}
					}
				}
				if (val2 != null)
				{
					if (oracleConnection == null || oracleConnection.CanReturnBestMatchingPR())
					{
						if (ProviderConfig.m_bTraceLevelPrivate)
						{
							Trace.Write(OracleTraceLevel.Private, OracleTraceTag.CP, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, null, "txnctx:get:res2"));
						}
						if (num2 == branchNum)
						{
							bMatchFound = true;
						}
						return val;
					}
					val2 = null;
					val = null;
				}
				else
				{
					val = null;
				}
				return null;
			}
			catch (Exception ex)
			{
				OracleException.HandleError(OracleTraceLevel.Private, (OracleTraceTag)268437504, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, ex);
				throw;
			}
			finally
			{
				string text = null;
				if (val == null)
				{
					text = "null";
				}
				else
				{
					text = "bMatchFound=F";
					if (val.m_mtsTxnCtx != null && val.m_mtsTxnCtx.m_mtsTxnBranch != null && val.m_mtsTxnCtx.m_mtsTxnBranch.BranchNumber == branchNum)
					{
						text = "bMatchFound=T";
					}
				}
				if (ProviderConfig.m_bTraceLevelPrivate)
				{
					Trace.Write(OracleTraceLevel.Private, (OracleTraceTag)2560, OracleTraceClassName.TransactionContext, OracleTraceFuncName.GetEnlisted, Trace.GetCPInfo(val as OracleConnectionImpl, m_localTxnId, null, null, bResourcePoolInfo: true) + $"TxnCtx.GetEnlisted(aff={affinityInstance};br={branchNum};bMustMatch={bMustMatch}) returning ({text})");
				}
			}
		}

		internal string GetListForTrace()
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (m_enlistedPRList != null)
			{
				for (int i = 0; i < 33; i++)
				{
					PR val = null;
					lock (this)
					{
						val = m_enlistedPRList[i];
					}
					if (val == null)
					{
						continue;
					}
					if (val.Dump())
					{
						if (val.m_sessionType != SessionType.Two_Session_Proxy)
						{
							stringBuilder.Append("(" + val.m_endUserSessionId + ":" + val.m_endUserSerialNum + ":" + val.m_instanceName + ")");
						}
						else
						{
							stringBuilder.Append(i + ":(sessid=" + val.m_endUserSessionId + "," + val.m_pxyUserSessionId + ");");
						}
					}
					else
					{
						stringBuilder.Append(i + ":(sessid=" + val.m_endUserSessionId + ":CLOSED);");
					}
				}
			}
			else
			{
				stringBuilder.Append("(null list!!!)");
			}
			return stringBuilder.ToString();
		}

		private bool CanReturnPR(PR pr, OracleConnection connRefForCriteria)
		{
			PR bestMatchingPR = null;
			uint bitRepForBestMatchPR = 0u;
			bool result = false;
			if (connRefForCriteria != null)
			{
				Pool<PM, CP, PR>.MatchCriteria(pr, ref bestMatchingPR, ref bitRepForBestMatchPR, connRefForCriteria, prForDTXN: true);
				if (bestMatchingPR != null && connRefForCriteria.CanReturnBestMatchingPR())
				{
					result = true;
				}
			}
			else
			{
				result = true;
			}
			return result;
		}
	}
}
